package org.dromara.system.kernel;

import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.bo.PayPolicyBo;
import org.dromara.system.domain.vo.PayPolicyVo;
import org.dromara.system.mapper.PayOrderMapper;
import org.dromara.system.service.IPayPolicyService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class PayPolicyKernel {

    private final RestHighLevelClient restHighLevelClient;
    private final IPayPolicyService policyService;
    private final PayOrderMapper payOrderMapper;
    @Value("${spring.elasticsearch.environment}")
    private String environment;

    /**
     * 验证国家
     *
     * @param policyId 策略主键
     * @param country  国家
     * @return 结果
     */
    public R<String> validateCountry(Long policyId, String country) {

        //获取风控策略配置
        if (policyId != null && StringUtils.isNotEmpty(country)) {
            //获取风控策略配置
            PayPolicyVo policy = policyService.queryById(policyId);
            return validateCountry(policy, country);
        }

        return R.ok();
    }

    public R<String> validateCountry(PayPolicyVo policy, String country) {
        if (StringUtils.isEmpty(country)) {
            return R.fail("Country is empty");
        }

        if (policy != null) {
            return validateCountry(policy.getCountryType(), policy.getCountryCode(), country);
        }

        return R.ok();
    }

    public R<String> validateCountry(Integer countryType, String countryCode, String country) {
        if ((Objects.equals(countryType, 1) || Objects.equals(countryType, 2)) && StringUtils.isNotEmpty(countryCode)) {
            if (StringUtils.isEmpty(country)) {
                return R.fail("Country is empty");
            }

            //根据CountryType验证国家 0 不校验  1 黑名单  2 白名单
            List<String> countryCodes = Arrays.stream(countryCode.split(",")).map(String::trim).toList();
            boolean inList = countryCodes.contains(country);
            boolean isAllowed = true;

            if (Objects.equals(countryType, 1)) {
                // 黑名单逻辑：如果国家在黑名单中，则不允许
                isAllowed = !inList;
            } else if (Objects.equals(countryType, 2)) {
                // 白名单逻辑：如果国家在白名单中，则允许
                isAllowed = inList;
            }
            // 如果国家不被允许，返回失败信息
            if (!isAllowed) {
                return R.fail("The country is not allowed");
            }
        }

        return R.ok();
    }

    /**
     * 验证订单风险字段
     *
     * @param policyId 策略主键
     * @param order    订单
     * @return 结果
     */
    public String validateOrderFields(Long policyId, PayOrder order) {

        //获取风控策略配置
        PayPolicyVo policy = policyService.queryById(policyId);
        if (policy != null && StringUtils.isNotEmpty(policy.getConfig())) {
            //解析配置
            List<PayPolicyBo.Config> polices = JSONArray.parseArray(policy.getConfig(), PayPolicyBo.Config.class);
            if (!polices.isEmpty()) {
                //拿到校验字段列表
                Set<String> fields = polices.stream().map(PayPolicyBo.Config::getValidField).collect(Collectors.toSet());
                if (!fields.isEmpty()) {
                    long time = System.currentTimeMillis();
                    Map<String, Map<Integer, Integer>> numMap = getIpNum(order, 24); //, fields
                    System.out.println("【高风险字段】查询时间： " + (System.currentTimeMillis() - time) + " 毫秒");

                    for (PayPolicyBo.Config config : polices) {
                        Map<Integer, Integer> map = numMap.get(config.getValidField());
                        if (map == null) {
                            continue;
                        }
                        Integer count = map.get(config.getStatus());
                        if (count == null) {
                            count = 0;
                        }
                        //次数超过
                        if (config.getCount() > 0 && count >= config.getCount()) {
                            return "相同" + config.getFieldName() + "24小时" + config.getStatusStr() + "超过" + config.getCount() + "次";//这是一个高风险订单
                        }
                    }
                }
            }
        }

        return null;
    }

    /**
     * 获取ip成功失败次数
     *
     * @param fields 字段
     * @param hour   小时
     * @return 根据字段分组
     */
    private Map<String, Map<Integer, Integer>> getIpNum(PayOrder order, Set<String> fields, Integer hour) {

        Map<String, Map<Integer, Integer>> result = new HashMap<>();

        //组装或者条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //时间在hour之后的数据
        boolQuery.filter(QueryBuilders.rangeQuery("createTime").gt(System.currentTimeMillis() - hour * 60 * 60 * 1000));
        boolQuery.filter(QueryBuilders.termQuery("appId", order.getAppId()));
        boolQuery.mustNot(QueryBuilders.termQuery("orderId", order.getOrderId()));

        //组装字段
        Class<?> orderClass = order.getClass();
        if (fields.size() == 1) {
            for (String str : fields) {
                try {
                    Field field = orderClass.getDeclaredField(str);//动态获取字段
                    field.setAccessible(true);// 允许访问私有字段
                    String value = (String) field.get(order);//获取字段值
                    result.put(str, new HashMap<>());
                    boolQuery.filter(QueryBuilders.termQuery(str + ".keyword", value));
                } catch (Exception ignore) {
                }
            }
        } else {
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
            for (String str : fields) {
                try {
                    Field field = orderClass.getDeclaredField(str);//动态获取字段
                    field.setAccessible(true);// 允许访问私有字段
                    String value = (String) field.get(order);//获取字段值
                    if (StringUtils.isEmpty(value) || (str.equals("customerIp") && Objects.equals(value, "none"))) {
                        continue;
                    }
                    //加入或者条件
                    shouldQuery.should(QueryBuilders.termQuery(str + ".keyword", value));
                    result.put(str, new HashMap<>());
                } catch (Exception ignore) {
                }
            }
            boolQuery.filter(shouldQuery);
        }

        //如果没有有效字段，则不查询
        if (result.isEmpty()) {
            return result;
        }

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQuery);

        for (String fieldName : result.keySet()) {
            // 为每个字段创建一个过滤聚合
            FilterAggregationBuilder filterAgg = AggregationBuilders.filter("filter_" + fieldName, QueryBuilders.existsQuery(fieldName));

            // 在过滤聚合内部添加按状态分组的术语聚合
            TermsAggregationBuilder statusAgg = AggregationBuilders.terms("status").field("status");
            filterAgg.subAggregation(statusAgg);
            searchSourceBuilder.aggregation(filterAgg);
        }

        searchSourceBuilder.size(0); // 设置返回结果数量为0，只获取聚合结果
        SearchRequest searchRequest = new SearchRequest("pay_order_" + environment);
        searchRequest.source(searchSourceBuilder);

        // 执行查询，获取结果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            for (String fieldName : result.keySet()) {
                ParsedFilter filter = aggregations.get("filter_" + fieldName);
                Aggregations aggs = filter.getAggregations();
                ((ParsedLongTerms) aggs.get("status")).getBuckets().forEach(bucket -> {
                    String key = bucket.getKeyAsString();
                    long docCount = bucket.getDocCount();
                    result.get(fieldName).put(Integer.parseInt(key), (int) docCount);
                });
            }
        } catch (Exception ignore) {}

        return result;
    }

    private Map<String, Map<Integer, Integer>> getIpNum(PayOrder order, Integer hour) {

        // 使用formatter将LocalDateTime对象转换为字符串
        String dateTime = LocalDateTime.now().minus(Duration.ofHours(hour)).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<Map<String, Object>> list = payOrderMapper.selectCountByCustomerEmail(order.getCustomerEmail(), dateTime, order.getAppId(), order.getOrderId());

        Map<String, Map<Integer, Integer>> result = new HashMap<>();
        Map<Integer, Integer> subMap = new HashMap<>();
        for (Map<String, Object> map : list) {
            Integer status = Integer.parseInt(map.get("status").toString());
            Integer count = Integer.parseInt(map.get("count").toString());
            subMap.put(status, count);
        }

        result.put("customerEmail", subMap);
        return result;
    }

    /**
     * 获取高风险数量
     *
     * @param order  订单
     * @param fields 字段
     * @return 根据字段分组
     */
    private Map<String, Map<Integer, Integer>> getHeightNum(PayOrder order, Set<String> fields) {

        Map<String, Map<Integer, Integer>> result = new HashMap<>();

        //组装或者条件
//        Class<?> orderClass = order.getClass();
//        for (String str : fields) {
//            try {
//                Field field = orderClass.getDeclaredField(str);//动态获取字段
//                field.setAccessible(true);// 允许访问私有字段
//                String value = (String) field.get(order);//获取字段值
//
//                //如果客户ip为none则不校验
//                if (StringUtils.isEmpty(value) || (str.equals("customerIp") && Objects.equals(value, "none"))) {
//                    continue;
//                }
//
//                RedisUtils.setCacheObject(OrderConstants.pay_height + str + , errorNumber, Duration.ofMinutes(lockTime));
//                result.put(str, new HashMap<>());
//            } catch (Exception ignore) {
//            }
//        }

        return result;
    }
}
